﻿using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using MediaInfoWrapper;
using System.Diagnostics;

namespace ConverTomp4
{
    public partial class frmMain : Form
    {
        private string filePath = string.Empty;
        private string folderPath = string.Empty;
        private string outputPath = string.Empty;
        private string fileName = string.Empty;
        private string videofps = string.Empty;
        private string videoExt = string.Empty;
        private Dictionary<string, string> audios = new Dictionary<string, string>();
        private Dictionary<string, string> audioTracks = new Dictionary<string, string>();
        private Dictionary<string, string> audioEncodeTracks = new Dictionary<string, string>();
        private Dictionary<string, string> subtitles = new Dictionary<string, string>();
        private Dictionary<string, string> subTracks = new Dictionary<string, string>();
        private Dictionary<string, string> subEncodeTracks = new Dictionary<string, string>();

        private int selectAudioID = 0;
        private int selectSubtitleID = 0;

        public frmMain()
        {
            InitializeComponent();
        }

        private void btnOpen_Click(object sender, EventArgs e)
        {
            if (rbnSingleFile.Checked)
            {
                //Load File
                if (DialogResult.OK.Equals(this.ofdSourceFile.ShowDialog()))
                {
                    if (ofdSourceFile.FileNames.Count() == 1)
                    {
                        filePath = this.ofdSourceFile.FileName;
                        this.txtFilePath.Text = filePath;
                        folderPath = Path.GetDirectoryName(filePath);
                        //string temp = Path.GetPathRoot(filePath);
                    }
                    else
                    {
                        //Multi Files select
                    }
                }
                else
                {
                }
            }
            else
            {
                //Load File
                if (DialogResult.OK.Equals(this.fbdFolder.ShowDialog()))
                {
                    this.txtFilePath.Text = fbdFolder.SelectedPath;

                    DirectoryInfo dicectoryInfo = new DirectoryInfo(fbdFolder.SelectedPath);

                    StringBuilder sb = new StringBuilder();

                    foreach (FileInfo fileInfo in dicectoryInfo.GetFiles("*.mkv"))
                    {
                        sb.Append(fileInfo.FullName);
                        sb.Append("\r\n");
                    }

                    this.rtxtDetail.Text = sb.ToString();

                    folderPath = fbdFolder.SelectedPath;
                }
            }
        }

        private void txtFilePath_TextChanged(object sender, EventArgs e)
        {
            ClearDictionary();
            if (rbnSingleFile.Checked)
            {
                MediaInfo mediainfo = new MediaInfo(filePath);
                this.rtxtDetail.Text = mediainfo.InfoStandard;

                foreach (AudioTrack aTrack in mediainfo.Audio)
                {
                    this.cbAudio.Items.Add(aTrack.ID + " : " + aTrack.LanguageString + " : " + aTrack.Format);
                }

                foreach (TextTrack tTrack in mediainfo.Text)
                {
                    this.cbSubtitle.Items.Add(tTrack.ID + " : " + tTrack.LanguageString + " : " + tTrack.Codec);
                }

                if (cbAudio.Items.Count != 0)
                {
                    this.cbAudio.SelectedIndex = 0;
                }

                if (cbSubtitle.Items.Count != 0)
                {
                    this.cbSubtitle.SelectedIndex = 0;
                }
            }
        }

        private void btnConvert_Click(object sender, EventArgs e)
        {
            if (rbnNormal.Checked)
            {
                if (!string.IsNullOrEmpty(this.txtDestination.Text))
                {
                    outputPath = this.txtDestination.Text.Trim();
                }

                if (rbnSingleFile.Checked)
                {
                    fileName = Path.GetFileNameWithoutExtension(filePath);
                    ConvertFile(filePath, 1);
                }
                else
                {
                    DirectoryInfo dicectoryInfo = new DirectoryInfo(this.txtFilePath.Text);

                    foreach (FileInfo fileInfo in dicectoryInfo.GetFiles("*.mkv"))
                    {
                        System.Threading.Thread.Sleep(5000);
                        fileName = Path.GetFileNameWithoutExtension(fileInfo.FullName);
                        filePath = fileInfo.FullName;
                        ConvertFile(fileInfo.FullName, 1);
                    }
                }
            }
            else if (rbnHDJ.Checked)
            {
                if (!string.IsNullOrEmpty(this.txtDestination.Text))
                {
                    outputPath = this.txtDestination.Text.Trim();
                }

                if (rbnSingleFile.Checked)
                {
                    fileName = Path.GetFileNameWithoutExtension(filePath);
                    ConvertFile(filePath, 2);
                }
                else
                {
                    if (string.IsNullOrEmpty(this.txtFilePath.Text))
                    {
                        MessageBox.Show("请先选择一个目录！");
                        return;
                    }

                    DirectoryInfo dicectoryInfo = new DirectoryInfo(this.txtFilePath.Text);

                    foreach (FileInfo fileInfo in dicectoryInfo.GetFiles("*.mkv"))
                    {
                        System.Threading.Thread.Sleep(5000);
                        fileName = Path.GetFileNameWithoutExtension(fileInfo.FullName);
                        filePath = fileInfo.FullName;
                        ConvertFile(fileInfo.FullName, 2);
                    }
                }
            }
            else if (rbnMovie.Checked)
            {


                if (!string.IsNullOrEmpty(this.txtDestination.Text))
                {
                    outputPath = this.txtDestination.Text.Trim();
                }

                if (rbnSingleFile.Checked)
                {
                    fileName = Path.GetFileNameWithoutExtension(filePath);
                    ConvertFile(filePath, 3);
                }
                else
                {
                    DirectoryInfo dicectoryInfo = new DirectoryInfo(this.txtFilePath.Text);

                    foreach (FileInfo fileInfo in dicectoryInfo.GetFiles("*.mkv"))
                    {
                        System.Threading.Thread.Sleep(5000);
                        fileName = Path.GetFileNameWithoutExtension(fileInfo.FullName);
                        filePath = fileInfo.FullName;
                        ConvertFile(fileInfo.FullName, 3);
                    }
                }
            }
            else
            {
                MessageBox.Show("You must choose a source type to continue !");
            }
        }

        private void ConvertFile(string srcFilePath, int sourceType)
        {
            string folderPath = Path.GetPathRoot(srcFilePath);

            selectAudioID = Convert.ToInt32(cboxAudio.Text.Split(':').ToList()[0].Trim());

            selectSubtitleID = Convert.ToInt32(cboxSubtitle.Text.Split(':').ToList()[0].Trim());

            MediaInfo mediainfo = new MediaInfo(srcFilePath);
            AnalyseFile(mediainfo);

            string videoTrack = folderPath + "temp." + videoExt;

            foreach (string key in audios.Keys)
            {
                audioTracks.Add(key, folderPath + "audio_Track_" + key + "." + audios[key]);
            }

            foreach (string key in subtitles.Keys)
            {
                subTracks.Add(key, folderPath + "title_Track_" + key + "." + subtitles[key]);
            }

            StringBuilder sb = new StringBuilder();

            sb.Append(ExtractFile(videoTrack));

            sb.Append("\r\n\r\n");

            if (sourceType == 1)
            {
                sb.Append(MergeFile(videoTrack));
            }
            else if (sourceType == 2)
            {
                sb.Append(EncodeAudio());
                sb.Append("\r\n\r\n");
                sb.Append(EncodeSubtitle());
                sb.Append("\r\n\r\n");
                sb.Append(MergeFile(videoTrack));
            }
            else if (sourceType == 3)
            {
                sb.Append(EncodeAudioForMovie());
                sb.Append("\r\n\r\n");
                sb.Append(EncodeSubtitleForMovie(this.txtSubtitle.Text.Trim()));
                sb.Append("\r\n\r\n");
                sb.Append(MergeFile(videoTrack));
            }
            else
            {
                MessageBox.Show("You must choose a source type !");
            }

            if (!string.IsNullOrEmpty(this.txtDestination.Text.Trim()))
            {
                sb.Append("\r\n\r\n");

                sb.Append(MoveFileToDestination());
            }

            StreamWriter sw = new StreamWriter(folderPath + "\\" + fileName + ".bat");
            sw.Write(sb.ToString());
            sw.Dispose();

            //DeleteTempFiles(videoTrack);

            Process proc = Process.Start(folderPath + "\\" + fileName + ".bat");

            if (proc != null)
            {
                proc.WaitForExit();
                DeleteTempFiles(videoTrack);
            }
        }

        #region Analyse File Information
        private void AnalyseFile(MediaInfo srcMediainfo)
        {
            videoExt = AnalyseVideo(srcMediainfo.Video[0]);
            videofps = srcMediainfo.Video[0].FrameRate;

            AnalyseAudio(srcMediainfo.Audio);

            AnalyseSubtitle(srcMediainfo);
        }

        private string AnalyseVideo(VideoTrack srcVideoTrack)
        {
            switch (srcVideoTrack.CodecID)
            {
                case "V_MPEG4/ISO/AVC": return "h264";
                case "V_REAL/*": return "rm";
                default: return "";
            }
        }

        private void AnalyseAudio(List<AudioTrack> srcAudioTracks)
        {
            if (selectAudioID == 0)
            {
                foreach (AudioTrack audio in srcAudioTracks)
                {
                    string aExt = string.Empty;

                    if (audio.CodecID.Contains("A_MPEG/L3"))
                    {
                        audios.Add(audio.ID, "mp3");
                    }
                    else if (audio.CodecID.Contains("A_AC3"))
                    {
                        audios.Add(audio.ID, "ac3");
                    }
                    else if (audio.CodecID.Contains("AAC"))
                    {
                        audios.Add(audio.ID, "aac");
                    }
                    else if (audio.CodecID.Contains("DTS"))
                    {
                        audios.Add(audio.ID, "dts");
                    }
                    else if (audio.CodecID.Contains("PCM") || audio.CodecID.ToLower().Contains("sowt"))
                    {
                        audios.Add(audio.ID, "wav");
                    }
                    else if (audio.CodecID.Contains("VOR"))
                    {
                        audios.Add(audio.ID, "ogg");
                    }
                    else if (audio.CodecID.Contains("REAL"))
                    {
                        audios.Add(audio.ID, "ra");
                    }
                    else
                    {
                        throw new Exception("unkown audio track on track " + audio.ID);
                    }
                    //add break to control the audio number
                    if (!rbnMovie.Checked)
                        break;
                }
            }
            else
            {
                AudioTrack audio = srcAudioTracks.Find(item => item.ID == selectAudioID.ToString());
                if (audio.CodecID.Contains("A_MPEG/L3"))
                {
                    audios.Add(audio.ID, "mp3");
                }
                else if (audio.CodecID.Contains("A_AC3"))
                {
                    audios.Add(audio.ID, "ac3");
                }
                else if (audio.CodecID.Contains("AAC"))
                {
                    audios.Add(audio.ID, "aac");
                }
                else if (audio.CodecID.Contains("DTS"))
                {
                    audios.Add(audio.ID, "dts");
                }
                else if (audio.CodecID.Contains("PCM") || audio.CodecID.ToLower().Contains("sowt"))
                {
                    audios.Add(audio.ID, "wav");
                }
                else if (audio.CodecID.Contains("VOR"))
                {
                    audios.Add(audio.ID, "ogg");
                }
                else if (audio.CodecID.Contains("REAL"))
                {
                    audios.Add(audio.ID, "ra");
                }
                else
                {
                    throw new Exception("unkown audio track on track " + audio.ID);
                }
            }
        }

        private void AnalyseSubtitle(MediaInfo srcMediaInfo)
        {
            if (selectSubtitleID == 0)
            {
                foreach (TextTrack text in srcMediaInfo.Text)
                {
                    if (text.Codec.Contains("UTF8"))
                    {
                        subtitles.Add(text.ID, "srt");
                    }
                    else if (text.Codec.Contains("SSA"))
                    {
                        subtitles.Add(text.ID, "ssa");
                    }
                    else if (text.Codec.Contains("ASS"))
                    {
                        subtitles.Add(text.ID, "ass");
                    }
                    else
                    {
                    }
                    //add break to control the subtitles number 
                    break;
                }
            }
            else
            {
                TextTrack text = srcMediaInfo.Text.Find(item => item.ID == selectSubtitleID.ToString());
                if (text.Codec.Contains("UTF8"))
                {
                    subtitles.Add(text.ID, "srt");
                }
                else if (text.Codec.Contains("SSA"))
                {
                    subtitles.Add(text.ID, "ssa");
                }
                else if (text.Codec.Contains("ASS"))
                {
                    subtitles.Add(text.ID, "ass");
                }
                else
                {
                }
            }
        }
        #endregion

        #region Generate the script

        private string ExtractFile(string videoTrack)
        {
            StringBuilder strCommand = new StringBuilder();

            strCommand.Append("\"" + Application.StartupPath + "\\mkvextract\\mkvextract.exe\" tracks \"" + filePath + "\" ");

            strCommand.Append("1:" + videoTrack + " ");

            foreach (string key in audioTracks.Keys)
            {
                strCommand.Append(key + ":" + audioTracks[key] + " ");
            }

            foreach (string key in subTracks.Keys)
            {
                strCommand.Append(key + ":" + subTracks[key] + " ");
            }

            return strCommand.ToString();
        }

        private string MergeFile(string videoTrack)
        {
            StringBuilder strCommand = new StringBuilder();

            strCommand.Append("\"" + Application.StartupPath + "\\MP4Box\\MP4Box.exe\" ");

            strCommand.Append("-add " + videoTrack + ":fps=" + videofps + " ");

            foreach (string key in audioTracks.Keys)
            {
                strCommand.Append("-add " + audioTracks[key] + " ");
            }

            foreach (string key in subTracks.Keys)
            {
                strCommand.Append("-add " + subTracks[key] + ":hdlr=sbtl:lang=eng ");
            }

            strCommand.Append("-new \"" + Path.GetDirectoryName(filePath) + "\\" + fileName + ".mp4\"");

            return strCommand.ToString();
        }

        private void DeleteTempFiles(string videoTrack)
        {
            if (File.Exists(videoTrack))
                File.Delete(videoTrack);

            foreach (string key in audioTracks.Keys)
            {
                if (File.Exists(audioTracks[key]))
                    File.Delete(audioTracks[key]);

                if (File.Exists(audioEncodeTracks[key]))
                    File.Delete(audioEncodeTracks[key]);

                if (File.Exists(Path.GetDirectoryName(audioTracks[key]) + Path.GetFileNameWithoutExtension(audioTracks[key]).Substring(0, Path.GetFileNameWithoutExtension(audioTracks[key]).Length - 4) + " - Log.txt"))
                    File.Delete(Path.GetDirectoryName(audioTracks[key]) + Path.GetFileNameWithoutExtension(audioTracks[key]).Substring(0, Path.GetFileNameWithoutExtension(audioTracks[key]).Length - 4) + " - Log.txt");
            }

            foreach (string key in subTracks.Keys)
            {
                if (File.Exists(subTracks[key]))
                    File.Delete(subTracks[key]);
                if (File.Exists(subEncodeTracks[key]))
                    File.Delete(subEncodeTracks[key]);
            }

            if (File.Exists(folderPath + "\\" + fileName + ".bat"))
                File.Delete(folderPath + "\\" + fileName + ".bat");

            ClearDictionary();
        }

        private void ClearDictionary()
        {
            audios.Clear();
            audioTracks.Clear();
            audioEncodeTracks.Clear();
            subtitles.Clear();
            subTracks.Clear();
            subEncodeTracks.Clear();
        }

        private string MoveFileToDestination()
        {
            return "move \"" + folderPath + "\\" + fileName + ".mp4\" \"" + this.txtDestination.Text + "\"";
        }

        #endregion

        private void btnDestinationFolder_Click(object sender, EventArgs e)
        {
            //Load File
            if (DialogResult.OK.Equals(this.fbdFolder.ShowDialog()))
            {
                this.txtDestination.Text = fbdFolder.SelectedPath;
            }
            else
            {
            }
        }

        private string EncodeAudio()
        {
            StringBuilder encodeStr = new StringBuilder();

            Dictionary<string, string> tempDic = new Dictionary<string, string>();

            foreach (string key in audioTracks.Keys)
            {
                string audioTrackName = Path.GetDirectoryName(audioTracks[key]) + Path.GetFileNameWithoutExtension(audioTracks[key]) + "_encoded.aac";
                encodeStr.Append("\"" + Application.StartupPath + "\\eac3to\\eac3to.exe\" " + audioTracks[key] + " " + audioTrackName + " -160 -down2 ");
                encodeStr.Append("\r\n\r\n");

                tempDic.Add(key, audioTrackName);
                audioEncodeTracks.Add(key, audioTrackName + ".m4a");
            }

            foreach (string key in tempDic.Keys)
            {
                string tmp;

                tmp = audioTracks[key];
                audioTracks[key] = audioEncodeTracks[key];
                audioEncodeTracks[key] = tmp;
            }

            return encodeStr.ToString();
        }

        private string EncodeAudioForMovie()
        {
            string selectKey = cbAudio.Text.Split(':')[0].Trim();

            StringBuilder encodeStr = new StringBuilder();

            Dictionary<string, string> tempDic = new Dictionary<string, string>();

            foreach (string key in audioTracks.Keys)
            {
                if (selectKey == key)
                {
                    string audioTrackName = Path.GetDirectoryName(audioTracks[key]) + Path.GetFileNameWithoutExtension(audioTracks[key]) + "_encoded.aac";
                    encodeStr.Append("\"" + Application.StartupPath + "\\eac3to\\eac3to.exe\" " + audioTracks[key] + " " + audioTrackName + " -160 -down2 ");
                    encodeStr.Append("\r\n\r\n");

                    tempDic.Add(key, audioTrackName);
                    audioEncodeTracks.Add(key, audioTrackName + ".m4a");
                }
                else
                {
                    tempDic.Add(key, "");
                }
            }

            foreach (string key in tempDic.Keys)
            {
                if (selectKey == key)
                {
                    string tmp;

                    tmp = audioTracks[key];
                    audioTracks[key] = audioEncodeTracks[key];
                    audioEncodeTracks[key] = tmp;
                }
                else
                {
                    audioTracks.Remove(key);
                }
            }

            return encodeStr.ToString();
        }

        private string EncodeSubtitle()
        {
            StringBuilder encodeStr = new StringBuilder();

            Dictionary<string, string> tempDic = new Dictionary<string, string>();

            foreach (string key in subTracks.Keys)
            {
                string subTrackName = Path.GetDirectoryName(subTracks[key]) + Path.GetFileNameWithoutExtension(subTracks[key]) + ".ttxt";
                encodeStr.Append("\"" + Application.StartupPath + "\\MP4Box\\MP4Box.exe\" -ttxt " + subTracks[key]);
                encodeStr.Append("\r\n\r\n");

                tempDic.Add(key, subTrackName);
                subEncodeTracks.Add(key, subTrackName);
            }

            foreach (string key in tempDic.Keys)
            {
                string tmp = subTracks[key];
                subTracks[key] = subEncodeTracks[key];
                subEncodeTracks[key] = tmp;
            }

            return encodeStr.ToString();
        }

        private string EncodeSubtitleForMovie(string filePath)
        {
            subtitles.Clear();
            subTracks.Clear();
            subEncodeTracks.Clear();

            StringBuilder encodeStr = new StringBuilder();

            string subTrackName = Path.GetPathRoot(filePath) + Path.GetFileNameWithoutExtension(filePath) + ".ttxt";
            encodeStr.Append("\"" + Application.StartupPath + "\\MP4Box\\MP4Box.exe\" -ttxt \"" + filePath + "\" -out " + subTrackName);
            encodeStr.Append("\r\n\r\n");


            subTracks.Add("2", subTrackName);
            subEncodeTracks.Add("2", subTrackName);

            return encodeStr.ToString();
        }

        private void rbnMovie_CheckedChanged(object sender, EventArgs e)
        {
            if (rbnMovie.Checked)
            {
                rtxtDetail.Visible = false;
                gboxTrackerSelector.Visible = true;
            }
            else
            {
                rtxtDetail.Visible = true;
                gboxTrackerSelector.Visible = false;
            }
        }

        private void chkSubtitle_CheckedChanged(object sender, EventArgs e)
        {
            if (chkSubtitle.Checked)
            {
                lblSusbitlePath.Visible = true;
                txtSubtitle.Visible = true;
                cbSubtitle.Enabled = false;
            }
            else
            {
                lblSusbitlePath.Visible = false;
                txtSubtitle.Visible = false;
                cbSubtitle.Enabled = true;
            }
        }

        private void btnOpenSubtitle_Click(object sender, EventArgs e)
        {
            //Load File
            if (DialogResult.OK.Equals(this.ofdSourceFile.ShowDialog()))
            {
                if (ofdSourceFile.FileNames.Count() == 1)
                {
                    this.txtSubtitle.Text = ofdSourceFile.FileName;
                }
                else
                {
                    MessageBox.Show("只能选择一个文件！");

                }
            }
            else
            {
            }
        }

        private void rbnHDJ_CheckedChanged(object sender, EventArgs e)
        {
            if (rbnFolder.Checked && rbnHDJ.Checked)
            {
                grpHDJSetting.Visible = true;

                if (!string.IsNullOrEmpty(this.txtFilePath.Text))
                {
                    DirectoryInfo dicectoryInfo = new DirectoryInfo(this.txtFilePath.Text);

                    FileInfo fileInfo = dicectoryInfo.GetFiles("*.mkv").First();

                    MediaInfo mediainfo = new MediaInfo(fileInfo.FullName);

                    if (mediainfo.AudioCount != 0)
                    {
                        foreach (AudioTrack aTrack in mediainfo.Audio)
                        {
                            cboxAudio.Items.Add(aTrack.ID + " : " + aTrack.Title);
                        }
                        cboxAudio.SelectedIndex = 0;
                    }
                    else
                    {
                        cboxAudio.Enabled = false;
                    }

                    if (mediainfo.TextCount != 0)
                    {
                        foreach (TextTrack tTrack in mediainfo.Text)
                        {
                            cboxSubtitle.Items.Add(tTrack.ID + " : " + tTrack.Title);
                        }
                        cboxSubtitle.SelectedIndex = 0;
                    }
                    else
                    {
                        cboxSubtitle.Enabled = false;
                    }
                }
            }
            else
            {
                grpHDJSetting.Visible = false;
            }
        }
    }
}
